﻿#region References
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidgren.Network;
using System.Threading;
using Ness.Engine.GUI;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.IO;
#endregion

namespace Ness.Engine.Net
{
    /// <summary>
    /// Main networking client class
    /// </summary>
    class Client
    {
        #region Public variables
        private NetClient client;
        public bool enabled = true;
        #endregion

        #region Private variables
        private Thread thread;
        private Engine game;
        #endregion

        public Client(Engine game, string id, string host, int port)
        {
            //Set some base variables
            this.game = game;

            //Initialise the main sclient instance
            Debugger.WriteColour("Attempting to connect to server: " + host + ":" + port, ConsoleColor.Blue);
            NetPeerConfiguration config = new NetPeerConfiguration(id);
            config.EnableMessageType(NetIncomingMessageType.DiscoveryResponse);

            client = new NetClient(config);
            client.Start();
            client.Connect(host, port);

            //Start the client thread
            //thread = new Thread(new ThreadStart(Run));
           // thread.Start();
        }

        public void Disconnect()
        {
            //Disconnect from the server
            client.Disconnect("Client shutting down");
        }

        public void Run()
        {
           // while (enabled)
           // {
                NetIncomingMessage msg;
                while ((msg = client.ReadMessage()) != null)
                {
                    switch (msg.MessageType)
                    {
                        case NetIncomingMessageType.DiscoveryResponse:
                            //Connect to first server found (quick-play)
                            client.Connect(msg.SenderEndpoint);
                            break;

                        case NetIncomingMessageType.ConnectionApproval:
                            Debugger.WriteColour("Connected to server. Listening for data.", ConsoleColor.Blue);
                            break;

                        case NetIncomingMessageType.VerboseDebugMessage:
                        case NetIncomingMessageType.DebugMessage:
                            Debugger.WriteColour(msg.ReadString(), ConsoleColor.Blue);
                            break;

                        case NetIncomingMessageType.WarningMessage:
                        case NetIncomingMessageType.ErrorMessage:
                        case NetIncomingMessageType.Error:
                            Debugger.WriteError(msg.ReadString());
                            break;

                        case NetIncomingMessageType.Data:
                            if (game.isDrawing == false)
                            {
                                try
                                {
                                    ParsePacket(msg);
                                    Console.WriteLine("new packet " + (new Random()).Next());
                                }
                                catch (Exception e)
                                {
                                    Debugger.WriteLine(e.Message);
                                }
                            }
                            break;
                    }
                }
          //  }

           // thread.Abort();
        }

        public void SendCameraPosition(Vector3 position)
        {
            //Send the camera position
            NetOutgoingMessage om = client.CreateMessage();
            Ness.Engine.Entities.Entity plent = game.playerList[game.playerID] as Ness.Engine.Entities.Entity;

            om.Write("PLAYERPOS");

            //Serialize player entity
           // Serialize.Matrix(plent.WorldMatrix, om);

            om.Write(position.X);
            om.Write(position.Y);
            om.Write(position.Z);

            //client.SendMessage(om, NetDeliveryMethod.ReliableOrdered);
        }

        public void SendPlayerKey(string key)
        {
            //Send the camera position
            NetOutgoingMessage om = client.CreateMessage();

            om.Write("PLAYERKEY");
            om.Write(key);

            client.SendMessage(om, NetDeliveryMethod.ReliableOrdered);
        }

        private void ParsePacket(NetIncomingMessage msg)
        {
            string header = msg.ReadString();
            switch (header)
            {
                case "LIGHTDATA":
                    //New light
                    game.AddLightToScene(new Microsoft.Xna.Framework.Vector3(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat()), new Color(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat()), new Color(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat()), msg.ReadFloat(), msg.ReadBoolean());
                    break;

                case "WELCUM":
                    //Set the player id
                    game.playerID = msg.ReadString();
                    string mapFile = msg.ReadString();
                    if (Ness.Engine.Entities.Map.Exists(mapFile))
                    {
                        Debugger.WriteColour("Player ID set: " + game.playerID, ConsoleColor.Blue);
                        NetOutgoingMessage entdataMsg = client.CreateMessage();
                        entdataMsg.Write("ENTDATA");
                        client.SendMessage(entdataMsg, NetDeliveryMethod.ReliableOrdered);
                    }
                    else
                    {
                        throw new Exception("Map does not exist: " + mapFile);
                    }
                    break;

                case "PLAYERDATA":
                    //Update player entities
                    string pid = msg.ReadString();
                    float posx = msg.ReadFloat();
                    float posy = msg.ReadFloat();
                    float posz = msg.ReadFloat();
                    string ent_id = msg.ReadString();
                    string playermodel = msg.ReadString();

                    if (game.playerList.Contains(pid))
                    {
                        if (!game.isDrawing)
                        {
                            //Update world matrix
                            Ness.Engine.Entities.Entity ent = game.playerList[pid] as Ness.Engine.Entities.Entity;

                            if (pid == game.playerID)
                            {
                                game.playerPos = new Vector3(posx, posy, posz);
                            }

                            //Get matrix data from message
                            if (!game.isDrawing) ent.worldMatrix.M11 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M12 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M13 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M14 = msg.ReadFloat();

                            if (!game.isDrawing) ent.worldMatrix.M21 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M22 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M23 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M24 = msg.ReadFloat();

                            if (!game.isDrawing) ent.worldMatrix.M31 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M32 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M33 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M34 = msg.ReadFloat();

                            if (!game.isDrawing) ent.worldMatrix.M41 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M42 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M43 = msg.ReadFloat();
                            if (!game.isDrawing) ent.worldMatrix.M44 = msg.ReadFloat();
                        }
                    }
                    else
                    {
                        if (!game.isDrawing)
                        {
                            //Create the new player and add to scene
                            Debugger.WriteColour("New player joined (" + pid + ")", ConsoleColor.Blue);
                            Entities.Entity ent = new Entities.Entity(game, playermodel, false);

                            game.playerList.Add(pid, ent);

                            game.AddToScene(ent);
                        }
                    }
                    break;

                case "ENTDATA":
                    if (!game.isDrawing)
                    {
                        //Update entity
                        string entityID = msg.ReadString();
                        string model = msg.ReadString();

                        if (game.clientEntities.Contains(entityID))
                        {
                            //Update world matrix
                            if (!game.isDrawing)
                            {
                                Entities.Entity ent = game.clientEntities[entityID] as Entities.Entity;

                                //Get matrix data from message
                                if (!game.isDrawing) ent.worldMatrix.M11 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M12 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M13 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M14 = msg.ReadFloat();

                                if (!game.isDrawing) ent.worldMatrix.M21 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M22 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M23 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M24 = msg.ReadFloat();

                                if (!game.isDrawing) ent.worldMatrix.M31 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M32 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M33 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M34 = msg.ReadFloat();

                                if (!game.isDrawing) ent.worldMatrix.M41 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M42 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M43 = msg.ReadFloat();
                                if (!game.isDrawing) ent.worldMatrix.M44 = msg.ReadFloat();
                            }
                        }
                        else
                        {
                            //Create the new entity and add to scene
                            Entities.Entity ent = new Entities.Entity(game, model, false);
                            Console.WriteLine("new ent: " + model);
                            game.AddToScene(ent);
                        }
                    }
                    break;

                default:
                    Debugger.WriteError("Unhandled header: " + header);
                    break;
            }
        }
    }
}
